package dproxy.server;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import dproxy.server.cache.bo.CacheBO;
import dproxy.server.common.exceptions.AppInfrastructureException;

/**
 * A ServerLoader is responsible for reading server configuration 
 * and loading it's instances. Each dproxy-config.xml will generate a 
 * server instance.
 */
public class ServerLoader {

    /** Logger. */
    private Log logger = LogFactory.getLog(getClass());

    /** 
     * All servers loader by this application. Each dproxy-config xml and
     * each jar on loading directory generates a server instance.
     */
    private Collection<Server> servers = new ArrayList<Server>();

    /**
     * @param deployDirRoot Root of deployment directory. This loader will load
     * all JARS and .xml files and deploy it's contents
     */
    public ServerLoader(File deployDirRoot) {
        try {
            File[] deployDirContents = deployDirRoot.listFiles();
            if (deployDirContents != null) {
	            for (File deployFile : deployDirContents) {
	                if (deployFile.isFile()) {
	                    if (deployFile.getName().endsWith(".jar")) {
	                        URLClassLoader classLoader = new URLClassLoader(
	                                new URL[] { deployFile.toURL() });
	                        ZipFile deploymentJar = new ZipFile(deployFile);
	                        ZipEntry deplDesc = deploymentJar.getEntry("dproxy-config.xml");
	                        
	                        if (deplDesc == null) {
	                            logger.error(
	                                    deployFile.getName()
	                                    + " does not contain an dproxy-config.xml deployment descriptor");
	                        } else {
	                            // Load a server using JAR classloader and it's internal deployment descriptor
	                            InputStream configFileStream = deploymentJar.getInputStream(deplDesc);
	                            servers.add(
	                                    new Server(configFileStream,
	                                               classLoader));
	                        }
	                    } else if (deployFile.getName().endsWith(".xml")) {
	                        // Load a server using XML descriptor and application classloader
	                        servers.add(
	                                new Server(
	                                        new FileInputStream(deployFile),
	                                        this.getClass().getClassLoader()));
	                    } else {
	                        logger.info("Ignoring file " + deployFile.getName()
	                                + " : is not a JAR nor an XML");
	                    }
	                }
	            }
            }
        } catch (MalformedURLException e) {
            throw new AppInfrastructureException(e);
        } catch (FileNotFoundException e) {
            throw new AppInfrastructureException(e);
        } catch (ZipException e) {
            throw new AppInfrastructureException(e);
        } catch (IOException e) {
            throw new AppInfrastructureException(e);
        }
    }

    /**
     * Starts all servers.
     */
    public void start() {
        // Starts cache databases before starting listener threads
        CacheBO.getInstance().startUp();
        
        // Start every server
        for (Server server : servers) {
            server.start();
        }
    }

    /**
     * Stops all servers.
     */
    public void stop() {
        // Finish every server
        for (Server server : servers) {
            server.stop();
        }
    }
}